/**
 * 
 */
package furniture.render3d;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.vecmath.Point2d;
import javax.vecmath.Point3d;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;

/**
 * This objects responsible for rendering a set of triangles
 * to a Graphics2D object
 * @author Grill Balazs (balage.g@gmail.com)
 *
 */
public class Renderer {

	public Renderer(Projector projector){
		this.projector = projector;
	}
	
	/**
	 * The projector used to project 3d point into 2d.
	 */
	private Projector projector;
	
	public Projector getProjector() {
		return projector;
	}

	public void setProjector(Projector projector) {
		this.projector = projector;
	}

	/**
	 * Render "what" to "where". The Point (0,0,0) is drawn at the
	 * given center. All distances are multiplied by scale.
	 * @param what0	0
	 * @param where
	 * @param scale
	 * @param center
	 */
	public void render(TriangleSet what, GC where, double scale, Point center){
		List<Polygon3d> triangles = new ArrayList<Polygon3d>();
		triangles.addAll(what.getTriangles());
		Collections.sort(triangles, projector);
		for(Polygon3d t : triangles){
			int[] polygon = new int[t.getPoints().size()*2];
			int i = 0;
			for(Point3d point : t.getPoints()){
				Point2d p = projector.project(point);
				int x = (int)Math.round(p.x*scale+center.x);
				int y = (int)Math.round(p.y*scale+center.y);
				polygon[i*2] = x;
				polygon[i*2+1] = y;
				i++;
			}
			Color bg = new Color(where.getDevice(), t.getPaint());
			Color fg = new Color(where.getDevice(), t.getColor());
			where.setBackground(bg);
			where.setForeground(fg);
			
			if (t.fill()){
				where.fillPolygon(polygon);
			}//else
			where.drawPolygon(polygon);
			
			bg.dispose();
			fg.dispose();
		}
	}
	
	/**
	 * Create a BufferedImage and render the given triangles to it.
	 * @param what
	 * @param size
	 * @param scale
	 * @return
	 */
	public Image render(TriangleSet what, Point size, double scale){
		Image result = new Image(Display.getDefault(), size.x,size.y);
		GC gc = new GC(result);
		gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
		gc.fillRectangle(0, 0, size.x, size.y);
		render(what,gc,scale,new Point(size.x/2,size.y/2));
		return result;
	}
	
	/**
	 * Create a BufferedImage of the specified size and render the
	 * given triangles to it. This method detects scale and center
	 * automatically.
	 * @param what
	 * @param size
	 * @return
	 */
	public void render(GC gc, TriangleSet what, Point size){
		//detect bounds to display:
		Point2d min = new Point2d(0,0);
		Point2d max = new Point2d(0,0);
		Collection<Polygon3d> ts = what.getTriangles();
		for(Polygon3d q : ts){
			for(Point3d p : q.getPoints()){
				Point2d w = projector.project(p);
				min.x = Math.min(w.x, min.x);
				min.y = Math.min(w.y, min.y);
				max.x = Math.max(w.x, max.x);
				max.y = Math.max(w.y, max.y);
			}
		}
		
		double scale = Math.min(size.x/(max.x-min.x),size.y/(max.y-min.y));
		Point2d c = new Point2d((max.x+min.x)/2,(max.y+min.y)/2);
		Point center = new Point(size.x/2,size.y/2);
		center.x = center.x - (int)Math.round(c.x*scale);
		center.y = center.y - (int)Math.round(c.y*scale);
		
		gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
		gc.fillRectangle(0, 0, size.x, size.y);
		render(what,gc,scale,center);
	}
}
